package com.kelai.task.quartz;

import com.google.common.collect.Lists;
import com.kelai.utils.BreezeeUtils;
import org.apache.commons.lang3.StringUtils;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import java.text.ParseException;
import java.util.List;
import java.util.Map;
import java.util.Set;


public class QuartzSchedulerService {

    // @Qualifier
    private Scheduler quartzScheduler;

    public QuartzSchedulerService(Scheduler quartzScheduler) {
        this.quartzScheduler = quartzScheduler;
    }

    public QuartzSchedulerService(SchedulerFactoryBean schedulerFactory) {
        this.quartzScheduler = schedulerFactory.getScheduler();
    }

    /**
     * @Method addJob
     * @Description TODO
     * @param quartzConfig
     * @throws Exception
     * @return void
     * @since 1.0.0
     */
    public void addJob(QuartzConfigEntity quartzConfig) throws Exception {
        if (Job.class.isAssignableFrom(quartzConfig.obtainCls())) {// 添加继承了job类型的定时任务
            this.addJob(quartzConfig.getJobName(), quartzConfig.getGroupName(), quartzConfig.getCronExpression(), quartzConfig.obtainJobCls(),
                    quartzConfig.getDescription(), quartzConfig.getParameterMap());
        } else {// 代理方式添加定时任务
            this.addJob(quartzConfig.getJobName(), quartzConfig.getGroupName(), quartzConfig.getIsSerial(), quartzConfig.obtainCls(),
                    quartzConfig.getTargetMethod(), quartzConfig.getCronExpression(), quartzConfig.getDescription(), quartzConfig.getParameterMap());
        }
    }

    /**
     * @Method addJob
     * @Description TODO
     * @param jobName
     * @param jobGroup
     * @param cronExpression
     * @param jobCls
     * @param description
     * @param paramDataMap
     * @throws SchedulerException
     * @throws ParseException
     * @return void
     * @since 1.0.0
     */
    public void addJob(String jobName, String jobGroup, String cronExpression, Class<? extends Job> jobCls, String description,
            Map<String, Object> paramDataMap) throws SchedulerException, ParseException {
        // 创建jobDetail
        JobDetail jobDetail = this.buildJobDetail(jobName, jobGroup, jobCls, description, paramDataMap);
        // 创建trigger
        Trigger trigger = this.buildTrigger(jobName, jobGroup, cronExpression, description);
        // 添加任务
        this.addJob(jobDetail, trigger);
    }

    /**
     * job动态创建
     * 
     * @param jobName
     * @param jobGroup
     * @param isSerial
     * @param targetClass
     * @param targetMethod
     * @param cronExpression
     * @param paramDataMap
     * @throws SchedulerException
     * @throws ParseException
     */
    public void addJob(String jobName, String jobGroup, Boolean isSerial, Class<?> targetClass, String targetMethod, String cronExpression,
            String description, Map<String, Object> paramDataMap) throws Exception {
        // 创建jobDetail
        JobDetail jobDetail = this.buildJobDetail(jobName, jobGroup, targetClass, description, paramDataMap, isSerial, targetMethod);
        // 创建trigger
        Trigger trigger = this.buildTrigger(jobName, jobGroup, cronExpression, description);
        // 添加任务
        this.addJob(jobDetail, trigger);
    }

    /**
     * @Method buildJobDetail
     * @Description TODO
     * @param jobName
     * @param groupName
     * @param jobCls
     * @param description
     * @param paramDataMap
     * @return
     * @throws SchedulerException
     * @return JobDetail
     * @since 1.0.0
     */
    private JobDetail buildJobDetail(String jobName, String groupName, Class<? extends Job> jobCls, String description,
            Map<String, Object> paramDataMap) throws SchedulerException {
        groupName = StringUtils.isEmpty(groupName) ? Scheduler.DEFAULT_GROUP : groupName;
        JobKey jobkey = new JobKey(jobName, groupName);
        JobBuilder jobBuilder = JobBuilder.newJob(jobCls);
        jobBuilder.withIdentity(jobkey);
        jobBuilder.withDescription(description);
        JobDetail jobDetail = jobBuilder.build();
        jobDetail.getJobDataMap().putAll(paramDataMap);
        return jobDetail;
    }

    /**
     * @Method buildJobDetail
     * @Description TODO
     * @param jobName
     * @param groupName
     * @param cls
     * @param description
     * @param paramDataMap
     * @param isSerial
     * @param targetMethod
     * @return
     * @throws Exception
     * @return JobDetail
     * @since 1.0.0
     */
    private JobDetail buildJobDetail(String jobName, String groupName, Class<?> cls, String description, Map<String, Object> paramDataMap,
            Boolean isSerial, String targetMethod) throws Exception {
        // JobKey jobkey = new JobKey(jobName, groupName);
        // TriggerKey triggerKey = new TriggerKey(jobName, groupName);
        // 验证job或者触发器是否存在
        // if (quartzScheduler.checkExists(triggerKey) || quartzScheduler.checkExists(jobkey)) {
        // throw new SchedulerException("Add job fail, job exists!");
        // }
        groupName = StringUtils.isBlank(groupName) ? Scheduler.DEFAULT_GROUP : groupName;
        MethodInvokingJobDetailFactoryBean jobDetailFactory = new MethodInvokingJobDetailFactoryBean();
        jobDetailFactory.setName(jobName);
        jobDetailFactory.setGroup(groupName);
        jobDetailFactory.setConcurrent(isSerial);
        // jobDetailFactory.setTargetClass(cls);
        jobDetailFactory.setTargetObject(cls.newInstance());
        jobDetailFactory.setTargetMethod(targetMethod);
        jobDetailFactory.afterPropertiesSet();
        JobDetail jobDetail = jobDetailFactory.getObject();
        jobDetail.getJobBuilder().withDescription(description);
        // 设置参数
        jobDetail.getJobDataMap().putAll(paramDataMap);
        return jobDetail;
    }

    /**
     * @Method buildTrigger
     * @Description TODO
     * @param jobName
     * @param groupName
     * @param cronExpression
     * @param description
     * @return
     * @throws ParseException
     * @return Trigger
     * @throws SchedulerException
     * @since 1.0.0
     */
    private Trigger buildTrigger(String jobName, String groupName, String cronExpression, String description) throws ParseException,
            SchedulerException {
        groupName = StringUtils.isBlank(groupName) ? Scheduler.DEFAULT_GROUP : groupName;
        TriggerKey triggerKey = new TriggerKey(jobName, groupName);
        // 创建trigger
        TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
        triggerBuilder.withIdentity(triggerKey);
        triggerBuilder.withDescription(description);
        // triggerBuilder.startAt(triggerStartTime);开始执行时间
        // triggerBuilder.endAt(triggerEndTime);//结束执行时间

        // 2014-8-15这一天不执行任何任务
        // Calendar c = new GregorianCalendar(2014, 7, 15);
        // AnnualCalendar cal = new AnnualCalendar();
        // cal.setDayExcluded(c, true);
        // quartzScheduler.addCalendar("exclude", cal, false, false);
        // HolidayCalendar holidayCal = new HolidayCalendar();
        // holidayCal.addExcludedDate(new Date()); // 排除该日期
        // quartzScheduler.addCalendar("calendar", holidayCal, true, false);
        // triggerBuilder.modifiedByCalendar("calendar")//排除什么时候不执行
        CronExpression ce = new CronExpression(cronExpression);
        triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(ce));
        // SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever();
        Trigger trigger = triggerBuilder.build();
        // SimpleTrigger simpleTrigger = new SimpleTrigger("simpleTrigger", Scheduler.DEFAULT_GROUP);
        //
        // simpleTrigger.setStartTime(new Date(System.currentTimeMillis()));//设置启动时间
        // simpleTrigger.setRepeatInterval(5000);//设置重复间隔
        // simpleTrigger.setRepeatCount(10);//设置重复次数
        // TriggerUtils.
        return trigger;
    }

    /**
     * 添加job
     * 
     * @param jobDetail
     * @param newtrigger
     * @throws SchedulerException
     */
    private void addJob(JobDetail jobDetail, Trigger newtrigger) throws SchedulerException {
        CronTrigger trigger = (CronTrigger) quartzScheduler.getTrigger(newtrigger.getKey());
        if (BreezeeUtils.isEmpty(trigger)) {
            quartzScheduler.scheduleJob(jobDetail, newtrigger);
        } else {// Trigger已存在，那么更新相应的定时设置
            // 按新的trigger重新设置job执行
            quartzScheduler.rescheduleJob(newtrigger.getKey(), newtrigger);
        }
    }

    /**
     * 运行中的任务
     * 
     * @return
     * @throws SchedulerException
     */
    public List<QuartzConfigEntity> getCurrentlyExecutingJobs() throws SchedulerException {
        QuartzConfigEntity quartzConfig = null;
        List<QuartzConfigEntity> quartzConfigList = Lists.newArrayList();
        List<JobExecutionContext> executingJobs = quartzScheduler.getCurrentlyExecutingJobs();
        for (JobExecutionContext executingJob : executingJobs) {
            JobDetail jobDetail = executingJob.getJobDetail();
            Trigger trigger = executingJob.getTrigger();
            JobKey jobKey = jobDetail.getKey();
            quartzConfig = bulidQuartzConfigBean(jobKey, trigger, jobDetail);
            quartzConfigList.add(quartzConfig);
        }
        return quartzConfigList;
    }

    /**
     * 获取所有计划中的任务
     * 
     * @return
     * @throws SchedulerException
     */
    public List<QuartzConfigEntity> getQuartzConfigList() throws SchedulerException {
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        Set<JobKey> jobKeys = quartzScheduler.getJobKeys(matcher);
        List<QuartzConfigEntity> quartzConfigList = Lists.newArrayList();
        JobDetail jobDetail = null;
        QuartzConfigEntity quartzConfig = null;
        for (JobKey jobKey : jobKeys) {
            List<? extends Trigger> triggers = quartzScheduler.getTriggersOfJob(jobKey);
            jobDetail = quartzScheduler.getJobDetail(jobKey);
            for (Trigger trigger : triggers) {
                quartzConfig = bulidQuartzConfigBean(jobKey, trigger, jobDetail);
                quartzConfigList.add(quartzConfig);
            }
        }
        return quartzConfigList;
    }

    /**
     * @Method bulidQuartzConfigBean
     * @Description TODO
     * @param jobKey
     * @param trigger
     * @param jobDetail
     * @return
     * @throws SchedulerException
     * @return QuartzConfigEntity
     * @since 1.0.0
     */
    private QuartzConfigEntity bulidQuartzConfigBean(JobKey jobKey, Trigger trigger, JobDetail jobDetail) throws SchedulerException {
        String description = StringUtils.isBlank(trigger.getDescription()) ? jobDetail.getDescription() : trigger.getDescription();
        QuartzConfigEntity quartzConfig = new QuartzConfigEntity();
        quartzConfig = new QuartzConfigEntity();
        quartzConfig.setClassPath(jobDetail.getJobClass().getName());
        quartzConfig.setJobName(jobKey.getName());
        quartzConfig.setGroupName(jobKey.getGroup());
        quartzConfig.setDescription(description);
        quartzConfig.setJobStatus(quartzScheduler.getTriggerState(trigger.getKey()));
        String cronExpression = null;
        // 判断trigger
        if (trigger instanceof SimpleTrigger) {
            SimpleTrigger simple = (SimpleTrigger) trigger;
            cronExpression = "重复次数:" + (simple.getRepeatCount() == -1 ? "无限" : simple.getRepeatCount()) + ",重复间隔:"
                    + (simple.getRepeatInterval() / 1000L);
        }
        if (trigger instanceof CronTrigger) {
            CronTrigger cronTrigger = (CronTrigger) trigger;
            cronExpression = cronTrigger.getCronExpression();
        }
        quartzConfig.setCronExpression(cronExpression);
        return quartzConfig;
    }

    /**
     * @Method setStatus
     * @Description 设置状态
     *              (NONE, NORMAL, PAUSED, COMPLETE, ERROR, BLOCKED)
     *              None：Trigger已经完成，且不会在执行，或者找不到该触发器，或者Trigger已经被删除 NORMAL:正常状态 PAUSED：暂停状态 COMPLETE：触发器完成，但是任务可能还正在执行中 BLOCKED：线程阻塞状态 ERROR：出现错误
     * @param quartzConfig
     * @return void
     * @since 1.0.0
     */
    public void setStatus(QuartzConfigEntity quartzConfig) {
        TriggerKey triggerKey = new TriggerKey(quartzConfig.getJobName(), quartzConfig.getGroupName());
        try {
            quartzConfig.setJobStatus(quartzScheduler.getTriggerState(triggerKey));
        } catch (SchedulerException e) {
            quartzConfig.setJobStatus(Trigger.TriggerState.ERROR);
        }
    }

    /**
     * @Title: updateCronExpression
     * @Description: 更新触发器
     * @param quartzConfig
     * @throws SchedulerException
     * @throws ParseException
     */
    public void updateCronExpression(QuartzConfigEntity quartzConfig) throws SchedulerException, ParseException {
        TriggerKey triggerKey = new TriggerKey(quartzConfig.getJobName(), quartzConfig.getGroupName());
        if (quartzScheduler.checkExists(triggerKey)) {
            TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
            triggerBuilder.withIdentity(triggerKey);
            triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(quartzConfig.getCronExpression()));
            Trigger trigger = triggerBuilder.build();
            quartzScheduler.rescheduleJob(triggerKey, trigger);
            // quartzScheduler.resumeTrigger(triggerKey);//重启触发器
        }
    }

    /**
     * 立即执行定时任务
     * 
     * @param quartzConfig
     * @throws SchedulerException
     */
    public void execJob(QuartzConfigEntity quartzConfig) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(quartzConfig.getJobName(), quartzConfig.getGroupName());
        quartzScheduler.triggerJob(jobKey);
    }

    /**
     * @Title: removeJob
     * @Description: 移除定时任务
     * @param quartzConfig
     */
    public void removeJob(QuartzConfigEntity quartzConfig) throws SchedulerException {
        JobKey jobkey = new JobKey(quartzConfig.getJobName(), quartzConfig.getGroupName());
        TriggerKey triggerKey = new TriggerKey(quartzConfig.getJobName(), quartzConfig.getGroupName());
        quartzScheduler.pauseTrigger(triggerKey);// 停止触发器
        quartzScheduler.unscheduleJob(triggerKey);// 移除触发器
        quartzScheduler.deleteJob(jobkey);// 删除任务
    }

    /**
     * @Title: pauseJob
     * @Description: 停止定时任务
     * @param quartzConfig
     */
    public void pauseJob(QuartzConfigEntity quartzConfig) throws SchedulerException {
        JobKey jobkey = new JobKey(quartzConfig.getJobName(), quartzConfig.getGroupName());
        quartzScheduler.pauseJob(jobkey);
    }

    /**
     * @Title: resumeJob
     * @Description: 恢复相关的job任务
     * @param quartzConfig
     * @throws SchedulerException
     */
    public void resumeJob(QuartzConfigEntity quartzConfig) throws SchedulerException {
        JobKey jobkey = new JobKey(quartzConfig.getJobName(), quartzConfig.getGroupName());
        if (quartzScheduler.checkExists(jobkey)) {
            quartzScheduler.resumeJob(jobkey);
        }
    }

    /**
     * 暂停调度中所有的job任务
     * 
     * @throws SchedulerException
     */
    public void pauseAll() throws SchedulerException {
        quartzScheduler.pauseAll();
    }

    /**
     * 恢复调度中所有的job的任务
     * 
     * @throws SchedulerException
     */
    public void resumeAll() throws SchedulerException {
        quartzScheduler.resumeAll();
    }

    /**
     * @Title: start
     * @Description: 启动定时任务控制器
     * @throws SchedulerException
     */
    public void start() throws SchedulerException {
        if (!quartzScheduler.isStarted()) {
            quartzScheduler.start();
        }
    }

    /**
     * @Title: shutdown
     * @Description: 停止定时任务控制器
     * @throws SchedulerException
     */
    public void shutdown() throws SchedulerException {
        if (!quartzScheduler.isShutdown()) {
            quartzScheduler.shutdown();
        }
    }

}
